﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.IO;
using System.Linq;
using System.Threading;
using FileMonitor.Configuration.Prototype.BizProcess;
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;
namespace FileMonitor.Service.Provider.FileMonitor
{
  
    public abstract class MonitorBase:ProviderBase,IDisposable
    {
        protected NameValueCollection AllConfig;
        private MonitorProcessInfo processInfo;
        private System.IO.FileSystemWatcher fileWatcher;
        private bool moving_Mutex = false;
        private object objMutex = new object();
        public Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase exchangeDB;
        public string statusMsg = "";
        System.Threading.Timer timer;
        protected SplitFlag splitFlag = SplitFlag.CHAR;
        protected Dictionary<int, BizProcessBase> _dicBizProcesses;
        private Dictionary<Guid, int> failureTasksCount;
        protected Util util;

        #region Biz Process Logic
        public void CopyBizProcesses(Dictionary<int, BizProcessBase> sourceProcess)
        {
            if (this._dicBizProcesses == null)
            {
                this._dicBizProcesses = new Dictionary<int, BizProcessBase>();
            }
            this._dicBizProcesses.Clear();
            foreach (int key in sourceProcess.Keys)
            {
                this._dicBizProcesses.Add(key, sourceProcess[key].Copy());
                this._dicBizProcesses[key].Excuted += new BizProcessBase.ExcutedHander(FileMonitorBase_Excuted);
            }
        }

        void FileMonitorBase_Excuted(object sender, ExcutedEventArgs args)
        {
            if (args.Result == ExcutedResult.Successful)
            {
                UpdateBizSucessful(new Guid(args.Task["BatchId"]), args.NextStatus);
            }
            //When Biz runned failed,it will repeat 3 times.
            else if (args.Result == ExcutedResult.Failure)
            {
                Guid batchid = new Guid(args.Task["BatchId"]);
                if (this.failureTasksCount.Keys.Contains(batchid))
                {
                    int countTimes = this.failureTasksCount[batchid];
                    if (countTimes < 3)
                    {
                        NotifyRunBizProcess(args.Task);
                        this.failureTasksCount[batchid] = this.failureTasksCount[batchid] + 1;
                    }
                    else
                    {
                        UpdateBizFailure(batchid, -2);
                        this.failureTasksCount.Remove(batchid);
                    }
                }
                else
                {
                     NotifyRunBizProcess(args.Task);
                    this.failureTasksCount.Add(batchid, 1);
                }
            }
        }

        private void UpdateBizSucessful(Guid batchid,int status)
        {
            FileUtility.DBUtility.UpdateStatus(this.exchangeDB, batchid, status);
        }
        private void UpdateBizFailure(Guid batchid, int status)
        {
            FileUtility.DBUtility.UpdateStatus(this.exchangeDB, batchid, status);
        }

        protected void NotifyRunBizProcess(NameValueCollection task)
        {
            if (task["BatchId"] == null)
            {
                throw new ArgumentException("Can't not be null", "BatchId");
            }
            if (this._dicBizProcesses.Count > 0)
            {
                this._dicBizProcesses.First().Value.Enqueue(task);
            }
        }
        #endregion

        #region Public Memebers
        public MonitorProcessInfo ProcessInfo
        {
            get
            {
                return this.processInfo;
            }
        }

        #region ProviderBase Members
        public override string Description
        {
            get
            {
                return base.Description;
            }
        }

        public override string Name
        {
            get
            {
                return base.Name;
            }
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            base.Initialize(name, config);
            util = new Util();
            if (config["MonitorPath"] == null)
            {
                throw new ArgumentException("Must_Set_MonitorPath");
            }
            if (!string.IsNullOrEmpty(config["SplitArr"]))
            {
                splitFlag = SplitFlag.LENGTH;
                //config["SplitArr"]
            }
            exchangeDB = new SqlDatabase(System.Configuration.ConfigurationManager.ConnectionStrings["DefaultExchangeDB"].ConnectionString);
            AllConfig = config;
         
            this.failureTasksCount = new Dictionary<Guid, int>();
        }
        #endregion

        #endregion

        public void Dispose()
        {
            this.fileWatcher = null;
            this.timer.Dispose();
            //Insert a singnal to Biz thread for stop it
            this._dicBizProcesses.ToList().ForEach(p => p.Value.Enqueue(null));
        }

        #region Start Process
        internal void Start()
        {
            try
            {
                InitializeFileWatcher();
                InitializeMonitorProcessInfo();
                InitializeTimer();
            }
            catch (Exception ex)
            { }
        }

        private void InitializeFileWatcher()
        {
            if (this.fileWatcher == null)
            {
                fileWatcher = new System.IO.FileSystemWatcher();
                string monitorPath = this.AllConfig["MonitorPath"];
                util.EnsureCreateSubFolder(monitorPath, "temp");
                util.EnsureCreateSubFolder(monitorPath, "ignored");
                fileWatcher.Path = monitorPath + "\\temp";//this.AllConfig["MonitorPath"];
              //  Util.EnsureMoveTempFolderFiles(monitorPath + "\\temp", monitorPath);
                if (string.IsNullOrEmpty(this.AllConfig["Filter"]))
                {
                    fileWatcher.Filter = "*.*";
                }
                else
                {
                    fileWatcher.Filter = this.AllConfig["Filter"];
                }
            }
            if (!fileWatcher.EnableRaisingEvents)
            {
                fileWatcher.EnableRaisingEvents = true;
            }
            fileWatcher.InternalBufferSize = 16384;
            fileWatcher.Created += new System.IO.FileSystemEventHandler(fileWatcher_Created);
            fileWatcher.Changed += new System.IO.FileSystemEventHandler(fileWatcher_Changed);
            fileWatcher.Deleted += new System.IO.FileSystemEventHandler(fileWatcher_Deleted);

        }

        private void InitializeMonitorProcessInfo()
        {
            if (this.processInfo == null)
            {
                this.processInfo = new MonitorProcessInfo(this.Name, this.Description);
                this.processInfo.State = ProcessState.Starting;
                this.processInfo.MonitorPath = this.AllConfig["MonitorPath"];
            }
        }

        private void InitializeTimer()
        {

            if (this.timer == null)
            {
                timer = new Timer(new TimerCallback(MoveFile), this, 5, 2000);
            }
        }


        #endregion


        #region Stop Process
        internal void Stop()
        {
            this.processInfo.State = ProcessState.Pause;
            this.Dispose();
        }
        #endregion


        /// <summary>
        /// Timser's Delegate
        /// </summary>
        /// <param name="obj"></param>
        #region Move or Unzip File
        private void MoveFile(object obj)
        {
            try
            {
                if (!this.moving_Mutex)
                {
                    lock (this.objMutex)
                    {
                        if (!this.moving_Mutex)
                        {
                            this.moving_Mutex = true;
                           MonitorBase baseObj = obj as MonitorBase;
                            string path = baseObj.AllConfig["MonitorPath"];
                            string[] files = System.IO.Directory.GetFiles(path);
                            foreach (string fileName in files)
                            {
                                baseObj.util.MovePeocessFile(fileName, path, this.AllConfig);
                             //   MovePeocessFile(fileName, path);
                                AddToBeProcessFileInfo(fileName, path);
                            }
                            this.moving_Mutex = false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
              //  Log.Log.LogMessage(System.DateTime.Now.ToString()+"-------"+ex.Message, this.AllConfig["LogPath"]);
            }
        }
        #endregion

        #region FileWatcher Event Methods
        void fileWatcher_Deleted(object sender, System.IO.FileSystemEventArgs e)
        {
            this.ProcessDeleted(e);
        }

        void fileWatcher_Changed(object sender, System.IO.FileSystemEventArgs e)
        {
            this.ProcessChanged(e);
        }

        void fileWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
        {
            int waitTime = 1;
            Int32.TryParse(this.AllConfig["WaitTime"], out waitTime);
            if (waitTime == 0)
            {
                waitTime = 1;
            }
            System.Threading.Thread.Sleep(waitTime * 1000);
            try
            {

                if (System.IO.File.Exists(e.FullPath))
                {
                    if (this.processInfo.FileInfos.ContainsKey(e.Name))
                    {
                        this.processInfo.FileInfos[e.Name].State = FileState.Processing;
                    }
                    this.ProcessCreated(e);
                    string backupPath = this.AllConfig["BackUpPath"] + "\\" + e.Name;
                    if (System.IO.File.Exists(backupPath))
                    {
                        System.IO.File.Delete(backupPath);
                    }

                    if (System.IO.File.Exists(e.FullPath))
                    {
                        System.IO.File.Move(e.FullPath, this.AllConfig["BackUpPath"] + "\\" + e.Name);
                    }
                    if (this.processInfo.FileInfos.ContainsKey(e.Name))
                    {
                        this.processInfo.FileInfos[e.Name].State = FileState.Processed;
                        this.processInfo.FileInfos[e.Name].ProcessedDate = DateTime.Now;
                        this.processInfo.FileInfos[e.Name].CuurentFullPath = this.AllConfig["BackUpPath"] + "\\" + e.Name;
                    }
                }
            }
            catch (Exception ex)
            {
                string errorPath = e.FullPath + ".Error";
                if (System.IO.File.Exists(errorPath))
                {
                    System.IO.File.Delete(errorPath);
                }
                if (System.IO.File.Exists(e.FullPath))
                {
                    System.IO.File.Move(e.FullPath, e.FullPath + ".Error");
                }
                if (this.processInfo.FileInfos.ContainsKey(e.Name))
                {
                    this.processInfo.FileInfos[e.Name].State = FileState.Error;
                    this.processInfo.FileInfos[e.Name].CuurentFullPath = e.FullPath + ".Error";
                }
                // Log.Log.LogMessage(System.DateTime.Now.ToString() + "-------" +e.Name+"---------"+ex.Message, this.AllConfig["LogPath"]);
            }
        }
        #endregion
        
        #region 
        /// <summary>
        /// Process file after file Created in temp folder
        /// </summary>
        /// <param name="e"></param>
        public abstract void ProcessCreated(FileSystemEventArgs e);
        public abstract void ProcessChanged(FileSystemEventArgs e);
        public abstract void ProcessDeleted(FileSystemEventArgs e);
        #endregion

        #region Utility Methods
       
        private void AddToBeProcessFileInfo(string fileFullPath, string parentPath)
        {
            string actualFileName = fileFullPath.Substring(fileFullPath.LastIndexOf("\\") + 1);
            if (!this.processInfo.FileInfos.ContainsKey(actualFileName))
            {
                FileInfo fi = new FileInfo(actualFileName);
                fi.State = FileState.ToBeProcess;
                fi.CuurentFullPath = parentPath + "\\temp\\" + actualFileName;
                fi.InitialFullPath = parentPath + actualFileName;
                this.processInfo.FileInfos.Add(actualFileName, fi);
            }
        }

        #endregion

    }
}
